package edu.gmu.client;

import calendar.CalendarMgr;
import calendar.CalendarMgrStub;
import edu.gmu.messages.CreateManagerRequest;
import edu.gmu.messages.CreateManagerResponse;
import edu.gmu.messages.EchoRequest;
import edu.gmu.messages.EchoResponse;
import edu.gmu.messages.ReplicateRequest;
import edu.gmu.messages.ReplicateResponse;
import edu.gmu.messages.EndPoint;
import edu.gmu.messages.EnterRequest;
import edu.gmu.messages.EnterResponse;
import edu.gmu.messages.Neighbors;
import edu.gmu.messages.NeighborsRequest;
import edu.gmu.messages.NeighborsResponse;
import edu.gmu.messages.ExchangeRequest;
import edu.gmu.messages.ExchangeResponse;
import edu.gmu.messages.ListManagerResponse;
import edu.gmu.messages.ListManagerRequest;
import edu.gmu.messages.Topic;
import edu.gmu.middleware.Message;
import edu.gmu.middleware.RemoteStub;
import java.io.IOException;

/**
 * NOTE: STATELESS
 * ClientStub to comunicate with NamingService.  Note that the protocol
 * is stateless - each method call results in a new
 * connection-request+response-disconnect.
 * @author James Pope
 */
public class NamingServiceStub //implements CalendarMgrInt
{
    private EndPoint nameSeverEndpoint = null;
    
    /**
     * Creates a new NamingServiceStub.
     * @param nameSeverEndpoint
     */
    public NamingServiceStub( EndPoint nameSeverEndpoint )
    {
        this.nameSeverEndpoint = nameSeverEndpoint;
    }
    
    public EndPoint getEndpoint()
    {
        return this.nameSeverEndpoint;
    }

    
    /**
     * Utility to connect to nameServer - new connection each time
     * @retur nameServerStub or null if endpoint is unreachable
     */
    public static NamingServiceStub make( EndPoint endpoint )
    {
        NamingServiceStub stub = null;
        
        try
        {
            NamingServiceStub aStub = new NamingServiceStub(endpoint);
            int num = aStub.echo(665);
            if( num == 666 )
            {
                stub = aStub;
            }
        }
        catch(IOException ioe)
        {
            // ignore and return null
        }
        return stub;
    }
    
    /**
     * Utility to connect to nameServer - new connection each time
     * @retur nnameServerStub or null if endpoint is unreachable
     */
    private RemoteStub makeStub( EndPoint endpoint ) throws IOException
    {
        /*
         * First one that responds - connect and get list of managers
         */
        RemoteStub remoteStub = new RemoteStub( endpoint.address,
                                                endpoint.port,
                                                endpoint.port,
                                                endpoint.address );

        // To keep it simple - register for all each time
        remoteStub.register(Topic.CREATE_MANAGER_RESPONSE,CreateManagerResponse.class);
        remoteStub.register(Topic.LIST_MANAGER_RESPONSE,  ListManagerResponse.class);
        remoteStub.register(Topic.REPLICATE_RESPONSE,     ReplicateResponse.class);
        remoteStub.register(Topic.NEIGHBORS_RESPONSE,     NeighborsResponse.class);
        remoteStub.register(Topic.ENTER_RESPONSE,         EnterResponse.class);
        remoteStub.register(Topic.EXCHANGE_RESPONSE,      ExchangeResponse.class);
        remoteStub.register(Topic.ECHO_RESPONSE,          EchoResponse.class);
        
        return remoteStub;
    }
    
    
    //------------------------------------------------------------------------//
    // Interface
    //------------------------------------------------------------------------//
    public CalendarMgrStub createManager( int managerId ) throws IOException
    {
        // Connect
        RemoteStub stub = this.makeStub(nameSeverEndpoint);
        
        CreateManagerRequest request = new CreateManagerRequest(managerId);        
        Message message = stub.execute(Topic.CREATE_MANAGER_REQUEST, request);
        CreateManagerResponse response = (CreateManagerResponse)message;
        
        // Disconnect
        stub.stop();
        
        return new CalendarMgrStub(response.calendarMgrEndpoint);
    }
    
    public ListManagerResponse getManagers( ) throws IOException
    {
        // Connect
        RemoteStub stub = this.makeStub(nameSeverEndpoint);
        
        ListManagerRequest request = new ListManagerRequest();        
        Message message = stub.execute(Topic.LIST_MANAGER_REQUEST, request);
        ListManagerResponse response = (ListManagerResponse)message;
        
        // Disconnect
        stub.stop();
        
        return response;
    }
    
    public Neighbors getNeighbors() throws IOException
    {
        // Connect
        RemoteStub stub = this.makeStub(nameSeverEndpoint);
        
        NeighborsRequest request = new NeighborsRequest();
        Message message = stub.execute(Topic.NEIGHBORS_REQUEST, request);
        NeighborsResponse response = (NeighborsResponse)message;
        
        // Disconnect
        stub.stop();
        
        return response.neighbors;
    }
    
    public EnterResponse enterRequest( Neighbors neighbors ) throws IOException
    {
        // Connect
        RemoteStub stub = this.makeStub(nameSeverEndpoint);
        
        EnterRequest request = new EnterRequest();
        request.neighbors = neighbors;
        Message message = stub.execute(Topic.ENTER_REQUEST, request);
        EnterResponse response = (EnterResponse)message;
        
        // Disconnect
        stub.stop();
        
        return response;
    }
    
    public Neighbors exchange( Neighbors neighbors ) throws IOException
    {
        // Connect
        RemoteStub stub = this.makeStub(nameSeverEndpoint);
        
        ExchangeRequest request = new ExchangeRequest();
        request.neighbors = neighbors;
        Message message = stub.execute(Topic.EXCHANGE_REQUEST, request);
        ExchangeResponse response = (ExchangeResponse)message;
        
        // Disconnect
        stub.stop();
        
        return response.neighbors;
    }
    
    public CalendarMgrStub replicate( CalendarMgr mgr, byte[] file ) throws IOException
    {
        // Connect
        RemoteStub stub = this.makeStub(nameSeverEndpoint);
        
        ReplicateRequest request = new ReplicateRequest();
        request.primary = mgr.getEndPoint();
        request.id      = mgr.getId();
        request.file    = file;
        Message message = stub.execute(Topic.REPLICATE_REQUEST, request);
        ReplicateResponse response = (ReplicateResponse)message;
        
        CalendarMgrStub calMgrstub = new CalendarMgrStub( response.calendarMgrEndpoint );
        
        // Disconnect
        stub.stop();
        
        return calMgrstub;
    }
    
    public int echo( int num ) throws IOException
    {
        // Connect
        RemoteStub stub = this.makeStub(nameSeverEndpoint);
        
        EchoRequest request = new EchoRequest(num);
        Message message = stub.execute(Topic.ECHO_REQUEST, request);
        EchoResponse response = (EchoResponse)message;
        
        // Disconnect
        stub.stop();
        
        return response.num;
    }
    //------------------------------------------------------------------------//
    
    /**
     * Test main
     * @param args
     * @throws java.io.IOException
     */
    public static void main( String[] args ) throws IOException
    {
        String hostAddress = args[0];
        int port           = Integer.parseInt(args[1]);
        
        EndPoint endpoint = new EndPoint(hostAddress, port);
        
        NamingServiceStub stub = new NamingServiceStub( endpoint );
        Neighbors neighbors = stub.getNeighbors();
    }
}